package cn.seaboot.commons.core;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 雪花算法工厂
 * <p>
 * 用法与 self4j 一致
 *
 * @author Mr.css
 * @version 2024-12-03 9:29
 */
public class SnowflakeFactory {
    private static final Logger logger = LoggerFactory.getLogger(SnowflakeFactory.class);

    /**
     * 机房 ID
     */
    public static long datacenterId = 1;

    /**
     * 机器 ID
     */
    public static long workerId = 1;

    /**
     * 配置机房、机器唯一值，需要在集群中保证唯一，
     * <p>
     * 因为是静态函数，所以初始化非常早，甚至早于数据库查询。
     * <p>
     * 推荐做法，是将参数做到环境变量中，直接读取环境变量。
     * <p>
     * 注意：不要放到 spring 框架的 init() 函数中初始化，对代码整体风格有较大影响，而且会引入生命周期相关的问题。
     *
     * @param datacenterId 机房 ID
     * @param workerId     机器 ID
     */
    public static void init(long datacenterId, long workerId) {
        SnowflakeFactory.datacenterId = datacenterId;
        SnowflakeFactory.workerId = workerId;
        logger.debug("[Snowflake] using datacenterId: {}", datacenterId);
        logger.debug("[Snowflake] using workerId: {}", workerId);
    }

    /**
     * 三个推荐的启动方式
     * <p>
     * Program arguments / VM options / Environment variable
     * 通过启动命令设置，或者通过主机的环境变量设置
     *
     * @param args main 函数启动参数
     */
    public static void ofEnvironment(String[] args) {
        // 通过 Program arguments 设置（启动命令中添加）
        // E.G.：java -jar your-application.jar --datacenter=1 --worker=1）
        String datacenterId = null;
        String workerId = null;
        for (String arg : args) {
            if (arg.startsWith("--datacenter")) {
                datacenterId = arg.substring(arg.indexOf("=") + 1);
            } else if (arg.startsWith("--worker")) {
                workerId = arg.substring(arg.indexOf("=") + 1);
            }
        }

        // 通过 VM options 设置
        // E.G. java -Ddatacenter=1 -Dworker=1 MyApp
        if (CommonUtils.isEmpty(datacenterId) || CommonUtils.isEmpty(workerId)) {
            datacenterId = System.getProperty("datacenter");
            workerId = System.getProperty("worker");
        }

        // 通过 Environment variable 设置（环境变量）
        if (CommonUtils.isEmpty(datacenterId) || CommonUtils.isEmpty(workerId)) {
            datacenterId = System.getenv("datacenter");
            workerId = System.getenv("worker");
        }

        if (datacenterId != null && workerId != null) {
            SnowflakeFactory.init(Long.parseLong(datacenterId), Long.parseLong(workerId));
        }
    }

    /**
     * 创建一个雪花算法实例
     * <p>
     * 需要注意生命周期，这个类的初始化函数，不一定会优先于其它类；
     * 分布式环境下，不同节点之间的 ID 不允许一致；
     *
     * @return snowflake instance
     */
    public static Snowflake createSnowflake() {
        logger.debug("[Snowflake] create snowflake instance, using machine code: {}.{}", datacenterId, workerId);
        return new Snowflake(datacenterId, workerId);
    }
}
